/*
 * Copyright 2013-2019 Software Radio Systems Limited
 *
 * This file is part of srsLTE.
 *
 * srsLTE is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * srsLTE is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * A copy of the GNU Affero General Public License can be found in
 * the LICENSE file in the top-level directory of this distribution
 * and at http://www.gnu.org/licenses/.
 *
 */

#include "srslte/config.h"

#define MAKE_CALL(a)  CONCAT2(a,type_name)
#define MAKE_VEC(a)   CONCAT2(a,vec_suffix)
#define PRINT         CONCAT2(srslte_vec_fprint,print_suffix)

#ifdef LLR_IS_8BIT
#define llr_t int8_t
#define type_name _8bit
#define vec_suffix _bbb
#define print_suffix _bs
#define decptr h->dec8[h->current_dec]
#define dechdlr h->dec8_hdlr[h->current_dec]
#define input_is_interleaved 1
#else
#ifdef LLR_IS_16BIT
  #define llr_t int16_t
  #define vec_suffix _sss
  #define print_suffix _s
  #define decptr h->dec16[h->current_dec]
  #define dechdlr h->dec16_hdlr[h->current_dec]
  #define input_is_interleaved (h->current_dec > 0)
#define type_name _16bit
  #else
  #warning "Unsupported LLR mode"
  #endif
#endif

#define debug_enabled_iter 0
#define debug_len     20 

#define debug_vec(a) if (debug_enabled_iter) {printf("%s it=%d: ", STRING(a), n_iter);PRINT(stdout, a, debug_len);}


static void MAKE_CALL(extract_input_tail_sb)(llr_t *input, llr_t *syst, llr_t *app2, llr_t *parity0, llr_t *parity1, uint32_t long_cb)
{
  for (int i = long_cb; i < long_cb + 3; i++) {
    syst[i]    = input[3*(long_cb+32) + 2*(i - long_cb)];
    parity0[i] = input[3*(long_cb+32)+ 2*(i - long_cb) + 1];

    app2[i]    = input[3*(long_cb+32) + 6 + 2*(i - long_cb)];
    parity1[i] = input[3*(long_cb+32) + 6 + 2*(i - long_cb) + 1];
  }
}

/* Runs 1 turbo decoder iteration */
void MAKE_CALL(run_tdec_iteration)(srslte_tdec_t * h, llr_t * input)
{

  if (h->current_cbidx >= 0) {
    uint16_t *inter   = h->interleaver[h->current_inter_idx][h->current_cbidx].forward;
    uint16_t *deinter = h->interleaver[h->current_inter_idx][h->current_cbidx].reverse;
    llr_t *syst    = (llr_t*) h->syst0;
    llr_t *parity0 = (llr_t*) h->parity0;
    llr_t *parity1 = (llr_t*) h->parity1;

    llr_t *app1 = (llr_t*) h->app1;
    llr_t *app2 = (llr_t*) h->app2;
    llr_t *ext1 = (llr_t*) h->ext1;
    llr_t *ext2 = (llr_t*) h->ext2;

    uint32_t long_cb = h->current_long_cb;
    uint32_t n_iter  = h->n_iter;
    
    if (SRSLTE_TDEC_EXPECT_INPUT_SB && !h->force_not_sb && input_is_interleaved) {
      syst = input;
      // align to 32 bytes (warning: must be same alignment as in rm_turbo.c)
      parity0 = &input[long_cb+32];
      parity1 = &input[2*(long_cb+32)];
      if (n_iter == 0) {
        MAKE_CALL(extract_input_tail_sb)(input, syst, app2, parity0, parity1, long_cb);
      }
    } else {
      if (n_iter == 0) {
        decptr->tdec_extract_input(input, syst, app2, parity0, parity1, long_cb);
      }
    }
    
    if ((n_iter%2) == 0) {

      // Add apriori information to decoder 1
      if (n_iter) {
        MAKE_VEC(srslte_vec_sub)(app1, ext1, app1, long_cb);
      }

      // Run MAP DEC #1
      decptr->tdec_dec(dechdlr, syst, n_iter ? app1 : NULL, parity0, ext1, long_cb);

    }
    // Interleave extrinsic output of DEC1 to form apriori info for decoder 2
    if (n_iter%2) {
      // Convert aposteriori information into extrinsic information
      if (n_iter > 1) {
        MAKE_VEC(srslte_vec_sub)(ext1, app1, ext1, long_cb);
      }

      MAKE_VEC(srslte_vec_lut)(ext1, deinter, app2, long_cb);

      // Run MAP DEC #2. 2nd decoder uses apriori information as systematic bits
      decptr->tdec_dec(dechdlr, app2, NULL, parity1, ext2, long_cb);

      // Deinterleaved extrinsic bits become apriori info for decoder 1
     MAKE_VEC(srslte_vec_lut)(ext2, inter, app1, long_cb);

    }

    if (h->n_iter == 0) {
      debug_vec(syst);
      debug_vec(parity0);
      debug_vec(parity1);
    }
    debug_vec(ext1);
    debug_vec(ext2);
    debug_vec(app1);
    debug_vec(app2);

    h->n_iter++;
  } else {
    ERROR("Error CB index not set (call srslte_tdec_new_cb() first\n");
  }
}

#undef debug_enabled
#undef debug_len
#undef debug_vec
#undef llr_t
#undef vec_suffix
#undef print_suffix
#undef decptr
#undef dechdlr
#undef type_name
#undef input_is_interleaved